using System;
using System.Reflection;

namespace FreeFlow
{
  /// <summary>
  /// Attribute used to give textual descriptions to enums.
  /// </summary>
  [AttributeUsage(AttributeTargets.Field)]
  public sealed class EnumDescriptionAttribute : Attribute
  {
    /// <summary>
    /// Creates a new <see cref="EnumDescriptionAttribute"/> instance.
    /// </summary>
    /// <param name="text">Text.</param>
    public EnumDescriptionAttribute(string text)
    {
      this.text = text;
    }

    private string text;
    /// <summary>
    /// Gets the text description.
    /// </summary>
    public string Text
    {
      get
      {
        return text;
      }
    }
  }

  /// <summary>
  /// Helper class to get descriptions of enums.
  /// </summary>
  public static class EnumDescription
  {
    /// <summary>
    /// Gets the description of the specified enum. If there is no description attribute, returns the 
    /// enum as a string
    /// </summary>
    /// <param name="e">The enum to get the description of.</param>
    public static string GetDescription(Enum e)
    {
      string ret = e.ToString();
      Type t = e.GetType();
      MemberInfo[] members = t.GetMember(ret);
      if (members != null && members.Length == 1)
      {
        object[] attrs = members[0].GetCustomAttributes(typeof(EnumDescriptionAttribute), false);
        if (attrs.Length == 1)
        {
          ret = ((EnumDescriptionAttribute)attrs[0]).Text;
        }
      }
      return ret;
    }

    /// <summary>
    /// Gets an enum from its description.
    /// </summary>
    /// <param name="enumType">Enum type.</param>
    /// <param name="description">Enum description.</param>
    public static Enum GetEnum(Type enumType, string description)
    {
      MemberInfo[] members = enumType.GetMembers();
      foreach (MemberInfo info in members)
      {
        // if the passed-in description is the name of the Enum, return that
        if (info.Name == description)
          return (Enum)Enum.Parse(enumType, info.Name);

        // otherwise look at descriptions
        object[] attrs = info.GetCustomAttributes(typeof(EnumDescriptionAttribute), false);
        if (attrs.Length == 1)
        {
          if (((EnumDescriptionAttribute)attrs[0]).Text == description)
          {
            return (Enum)Enum.Parse(enumType, info.Name);
          }
        }
      }

      return null;
    }
  }
}
